package com.swt.gap.service.impl;

import com.swt.gap.common.NodeTypeEnum;
import com.swt.gap.common.graph.Graph;
import com.swt.gap.config.ExternalGraphConfig;
import com.swt.gap.model.vo.external.*;
import com.swt.gap.model.vo.graph.NodeProperty;
import com.swt.gap.service.ExternalGraphService;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 请填写说明
 *
 * @author sx
 * @date 2020/8/12 14:20
 */
@Service
public class ExternalGraphServiceImpl implements ExternalGraphService {

    private final ExternalGraphConfig externalGraphConfig;
    private final RestTemplate restTemplate;

    public ExternalGraphServiceImpl (ExternalGraphConfig externalGraphConfig,
                                     RestTemplate restTemplate) {
        this.externalGraphConfig = externalGraphConfig;
        this.restTemplate = restTemplate;

    }

    @Override
    public List<Graph.Node> searchExternalByNode(String name, Integer type, String ooid) {
        List<Graph.Node> retValue = new ArrayList<>();
        String searchNodeUrl = externalGraphConfig.getSearchNodeUrl();
        String type1 = externalGraphConfig.getType1();
        String type2 = externalGraphConfig.getType2();
        if (2 == type) {
            searchNodeUrl = searchNodeUrl.replace("{type}", type2);
        } else {
            searchNodeUrl = searchNodeUrl.replace("{type}", type1);
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getToken());

        List<SearchExternalNodeParam.Expresses> expresses = new ArrayList<>();
        if (!StringUtils.isEmpty(ooid)) {
            SearchExternalNodeParam.Expresses ooidExpress =SearchExternalNodeParam.Expresses.builder()
                    .dataType("String")
                    .key("ooId")
                    .isNumber(false)
                    .sign("=")
                    .value(ooid)
                    .build();
            expresses.add(ooidExpress);
        }
        SearchExternalNodeParam.Expresses express =SearchExternalNodeParam.Expresses.builder()
                .dataType("String")
                .key(2 == type ? externalGraphConfig.getName2() : externalGraphConfig.getName1())
                .isNumber(false)
                .sign("mh")
                .value(name)
                .build();
        expresses.add(express);
        SearchExternalNodeParam searchExternalNodeParam = SearchExternalNodeParam.builder()
                .needProperties(true)
                .expresses(expresses)
                .build();
        searchExternalNodeParam.setNeedProperties(true);

        HttpEntity<SearchExternalNodeParam> entity = new HttpEntity<>(searchExternalNodeParam, headers);
        SearchExternalNodeVO searchExternalNode = restTemplate.postForObject(searchNodeUrl, entity, SearchExternalNodeVO.class);
        searchExternalNode.getList().forEach(node -> {
            Map<String, Object> props = new HashMap<>();
            node.getProperties().forEach(prop -> {
                props.put(prop.getOoName(), prop.getValue());
            });
            List<String> labels = new ArrayList<>();
            labels.add(NodeTypeEnum.EXTERNAL.getName());
            labels.add(node.getOoName());

            Graph.Node retNode = Graph.Node.builder()
                    .id(node.getId())
                    .name(node.getName())
                    .type(type)
                    .labels(labels)
                    .properties(props)
                    .build();
            retValue.add(retNode);
        });
        return retValue;
    }


    @Override
    public Graph linkExternalData(String nodeId, String id, Integer type) {

        String nodeRelationshipUrl = externalGraphConfig.getNodeRelationshipUrl();
        String type1 = externalGraphConfig.getType1();
        String type2 = externalGraphConfig.getType2();
        if (2 == type) {
            nodeRelationshipUrl = nodeRelationshipUrl.replace("{type}", type2);
        } else {
            nodeRelationshipUrl = nodeRelationshipUrl.replace("{type}", type1);
        }
        nodeRelationshipUrl += id;
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getToken());
        HttpEntity<String> httpEntity = new HttpEntity<>(headers);

        ResponseEntity<ExternalGraphVO> result = restTemplate.exchange(nodeRelationshipUrl, HttpMethod.GET, httpEntity, ExternalGraphVO.class);
        ExternalGraphVO externalGraph = result.getBody();

        List<Graph.Link> links = new ArrayList<>();
        List<Graph.Node> nodes = new ArrayList<>();
        externalGraph.getLinks().forEach(link -> {
            Graph.Link retLink = new Graph.Link();
            String anotherNodeId = "";
            if (link.getSource().equals(id)) {
                retLink = Graph.Link.builder()
                        .id(link.getId())
                        .startNodeId(nodeId)
                        .endNodeId(link.getTarget())
                        .type(link.getOoName())
                        .build();
                anotherNodeId = link.getTarget();
            } else if (link.getTarget().equals(id)) {
                retLink = Graph.Link.builder()
                        .id(link.getId())
                        .startNodeId(link.getSource())
                        .endNodeId(nodeId)
                        .type(link.getOoName())
                        .build();
                anotherNodeId = link.getSource();
            }
            links.add(retLink);
            for(ExternalGraphVO.Node node: externalGraph.getNodes()) {
                if (node.getId().equals(anotherNodeId)) {
                    List<String> labels = new ArrayList<>();
                    labels.add(NodeTypeEnum.EXTERNAL.getName());
                    labels.add(node.getOoName());

                    Map<String, Object> retProps = new HashMap<>();
                    node.getProperties().forEach(prop -> {
                        retProps.put(prop.getOoName(), prop.getValue());
                    });
                    Graph.Node retNode = Graph.Node.builder()
                            .id(anotherNodeId)
                            .labels(labels)
                            .name(node.getName())
                            .properties(retProps)
                            .type(type)
                            .build();
                    nodes.add(retNode);
                    break;
                }
            }
        });
        return Graph.builder().nodes(nodes).links(links).build();
    }

    @Override
    public Graph expandExternalData(String id, Integer type, String mergedExternalId, String mergedId) {
        String nodeRelationshipUrl = externalGraphConfig.getNodeRelationshipUrl();
        String type1 = externalGraphConfig.getType1();
        String type2 = externalGraphConfig.getType2();
        if (2 == type) {
            nodeRelationshipUrl = nodeRelationshipUrl.replace("{type}", type2);
        } else {
            nodeRelationshipUrl = nodeRelationshipUrl.replace("{type}", type1);
        }
        nodeRelationshipUrl += id;
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getToken());
        HttpEntity<String> httpEntity = new HttpEntity<>(headers);

        ResponseEntity<ExternalGraphVO> result = restTemplate.exchange(nodeRelationshipUrl, HttpMethod.GET, httpEntity, ExternalGraphVO.class);
        ExternalGraphVO externalGraph = result.getBody();

        List<Graph.Link> links = new ArrayList<>();
        List<Graph.Node> nodes = new ArrayList<>();
        externalGraph.getLinks().forEach(link -> {
            String startNodeId = link.getSource();
            String endNodeId = link.getTarget();
            if (null != mergedExternalId && null != mergedId) {
                startNodeId = startNodeId.equals(mergedExternalId) ? mergedId : startNodeId;
                endNodeId = endNodeId.equals(mergedExternalId) ? mergedId : endNodeId;
            }
            Graph.Link retLink = Graph.Link.builder()
                    .id(link.getId())
                    .startNodeId(startNodeId)
                    .endNodeId(endNodeId)
                    .type(link.getOoName())
                    .build();
            links.add(retLink);
        });
        for(ExternalGraphVO.Node node: externalGraph.getNodes()) {
            if (null != mergedExternalId && node.getId().equals(mergedExternalId)) {
                continue;
            }
            List<String> labels = new ArrayList<>();
            labels.add(NodeTypeEnum.EXTERNAL.getName());
            labels.add(node.getOoName());

            Map<String, Object> retProps = new HashMap<>();
            node.getProperties().forEach(prop -> {
                retProps.put(prop.getOoName(), prop.getValue());
            });
            Graph.Node retNode = Graph.Node.builder()
                    .id(node.getId())
                    .labels(labels)
                    .name(node.getName())
                    .properties(retProps)
                    .type(type)
                    .build();
            nodes.add(retNode);
        }
        return Graph.builder().nodes(nodes).links(links).build();
    }

    @Override
    public List<NodeProperty> getNodeProperty(String id, Integer type) {
        List<NodeProperty> retValue = new ArrayList<>();
        String nodeUrl = externalGraphConfig.getNodeUrl();
        if (2 == type) {
            nodeUrl = nodeUrl.replace("{type}", externalGraphConfig.getType2());
        } else {
            nodeUrl = nodeUrl.replace("{type}", externalGraphConfig.getType1());
        }
        nodeUrl += id;
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getToken());
        HttpEntity<String> httpEntity = new HttpEntity<>(headers);
        ResponseEntity<ExternalNode> result = restTemplate.exchange(nodeUrl, HttpMethod.GET, httpEntity, ExternalNode.class);
        ExternalNode externalNode = result.getBody();
        if (null != externalNode.getProperties()) {
            externalNode.getProperties().forEach(p -> {
                retValue.add(NodeProperty.builder()
                        .propertyKey(p.getOoName())
                        .propertyValue(p.getValue())
                        .build());
            });
        }
        return retValue;
    }

    @Override
    public ExternalOntology getExternalOntology(Integer type) {
        String ontologyUrl = externalGraphConfig.getOntologyUrl();
        if (2 == type) {
            ontologyUrl = ontologyUrl.replace("{type}", externalGraphConfig.getType2());
        } else {
            ontologyUrl = ontologyUrl.replace("{type}", externalGraphConfig.getType1());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(getToken());
        HttpEntity<String> httpEntity = new HttpEntity<>(headers);
        ResponseEntity<ExternalOntology> result = restTemplate.exchange(ontologyUrl, HttpMethod.GET, httpEntity, ExternalOntology.class);
        return result.getBody();
    }

    private String getToken() {
        HashMap<String, Object> authParams = new HashMap<>();
        authParams.put("username", externalGraphConfig.getUsername());
        authParams.put("password", externalGraphConfig.getPassword());
        KgAuthVO kgAuthVO = restTemplate.postForObject(externalGraphConfig.getAuthUrl(), authParams, KgAuthVO.class);
        return kgAuthVO.getData().getIdToken();
    }

}
